package qc.module.qms.service.calc;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import qc.common.core.constants.QmsCacheConstant;
import qc.common.core.enums.qms.QmsPointTimeSegmentEnum;
import qc.common.core.enums.qms.QmsQueryPointDataStationRelationTypeEnum;
import qc.common.core.enums.qms.QmsQueryPointDataTimeSegmentTypeEnum;
import qc.module.qms.algorithm.NumericCalc.dto.QmsNumericCalcPointRelation;
import qc.module.qms.algorithm.dto.QmsPointDto;
import qc.module.qms.entity.*;
import qc.module.qms.mapper.QmsPointMapper;
import qc.module.qms.service.QmsPointService;
import qc.module.qms.service.QmsStationPointService;
import qc.module.qms.service.QmsStationRelationService;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * QMS数值计算点号与点号关联关系Service，根据计算方法和配置的输入点号数据序列动态生成和维护的点号与点号关联关系；
 * 用于快速查找点号需要触发的计算的关联点号（集合）、计算触发计算的时间段等公用方法
 * 此Service中不能调用QmsBasicConfigCacheGetService中的方法，因为在QmsBasicConfigCacheGetService中使用该类中的缓存接口
 * 此Service中需要用到其他缓存Service均通过直接调用对应缓存Service中的方法
 *
 * @author QuCheng Tech
 * @since 2025/9/30
 */
@Service
public class QmsNumericCalcPointRelationService {
    private static final Logger logger = LoggerFactory.getLogger(QmsNumericCalcPointRelationService.class);

    private static final String Cache_Key_Calc_Point_Relation = QmsCacheConstant.CACHE_KEY_CALC_POINT_RELATION_ENTITY;

    @Autowired
    private QmsPointService pointService;

    @Autowired
    private QmsStationRelationService stationRelationService;

    @Autowired
    private QmsStationPointService stationPointService;

    @Autowired
    private QmsCalcMethodRelatedPointService calcMethodPointService;

    @Autowired
    private QmsCalcMethodRelatedElementService calcMethodElementService;

    @Autowired
    private QmsCalcMethodService calcMethodService;

    @Autowired
    private QmsCalcMethodInputPointDataService calcMethodInputService;

    /**
     * 更新缓存，不管是否有缓存数据都进行更新
     *
     * @return java.util.List<qc.module.qms.algorithm.NumericCalc.dto.QmsNumericCalcPointRelation>
     * @author QuCheng Tech
     * @since 2025/9/30
     */
    @CachePut(value = Cache_Key_Calc_Point_Relation)//更新缓存，不管是否有缓存数据都进行更新
    public List<QmsNumericCalcPointRelation> cachePut() {
        logger.debug("cachePut");

        return initPointRelations();
    }

    /**
     * 清除缓存
     *
     * @return void
     * @author QuCheng Tech
     * @since 2025/9/30
     */
    @CacheEvict(value = Cache_Key_Calc_Point_Relation)//清除缓存
    public void cacheEvict() {
        logger.debug("cacheEvict");
    }

    /**
     * 获取全部；有缓存时从缓存获取，没有缓存时重新构建关联关系并放入缓存
     *
     * @return java.util.List<qc.module.qms.algorithm.NumericCalc.dto.QmsNumericCalcPointRelation>
     * @author QuCheng Tech
     * @since 2025/9/30
     */
    @Cacheable(value = Cache_Key_Calc_Point_Relation)
    public List<QmsNumericCalcPointRelation> getAllWithCache() {
        logger.debug("getAllWithCache");

        return initPointRelations();
    }

    /**
     * 初始化所有点号与触发点号关联关系
     *
     * @return java.util.List<qc.module.qms.algorithm.NumericCalc.dto.QmsNumericCalcPointRelation>
     * @author QuCheng Tech
     * @since 2025/9/30
     */
    public List<QmsNumericCalcPointRelation> initPointRelations() {
        logger.debug("initPointRelations 重新初始化构建点号与点号之间关联关系");

        List<QmsNumericCalcPointRelation> result = new ArrayList<>();

        //遍历所有点号，逐个获取计算方法--关联的输入点号数据--解析出点号后放入关联关系集合中
        List<QmsPoint> allPoints = pointService.getAllWithCache();
        if (allPoints != null && allPoints.size() > 0x0) {
            //遍历所有点号
            for (QmsPoint destPoint : allPoints) {
                if (destPoint.getTmsegs() != null && destPoint.getTmsegs().intValue() > 0x0) {
                    //点号的时段类型集合值有效
                    String destPointid = destPoint.getPointid();
                    String destPointStcd = destPoint.getStcd();
                    String destPointElementCode = destPoint.getElecode();
                    int destPointTmsegs = destPoint.getTmsegs().intValue();
                    QmsPointDto destPointDto = QmsPointMapper.MAPPER.toDto(destPoint);
                    //遍历点号的时段类型集合
                    for (QmsPointTimeSegmentEnum destTmseg : QmsPointTimeSegmentEnum.values()) {
                        if (destTmseg != QmsPointTimeSegmentEnum.UN_DEFINE && (destPointTmsegs & destTmseg.getIndex()) == destTmseg.getIndex()) {
                            //时段类型有效，并且点号的所有时段类型值中包含该时段类型
                            QmsCalcMethod useMethod = this.getPointCalcMethod(destPointDto, destTmseg);
                            //计算方法不为空时，不管计算方法中配置的输入数据数量，直接获取计算方法的关联输入配置项
                            if (useMethod != null) {
                                logger.debug("initPointRelations 根据计算方法初始化点号关联关系，点号[" + destPointid + "]，时段类型[" + destTmseg + "]，计算方法编码[" + useMethod.getMethodcode() + "]，计算方法名称[" + useMethod.getMethodname() + "]");
                                //计算方法有效，并且计算方法配置的输入点号有效，遍历逐个输入点号配置获取最终使用的点号
                                //输入数据的顺序按照计算方法关联的输入数据中指定的顺序，在此进行排序后再遍历获取点号数据
                                List<QmsCalcMethodInput> methodInputs = this.getCalcMethodInputs(useMethod.getMethodcode());
                                if (methodInputs != null && methodInputs.size() > 0x0) {
                                    List<QmsCalcMethodInput> sortedMethodInputs = methodInputs.stream().sorted(Comparator.comparingInt(QmsCalcMethodInput::getOdr)).collect(Collectors.toList());
                                    //记录输入时序数据的序号，从1开始
                                    int inputTsDatasConfigIndex = 0x0;
                                    for (QmsCalcMethodInput inputTsDatasConfig : sortedMethodInputs) {
                                        inputTsDatasConfigIndex++;
                                        //调试输出输入数据配置信息
                                        logger.debug("initPointRelations 根据计算方法和计算目标对象获取第[" + inputTsDatasConfigIndex + "]个输入点号数据序列，输入数据配置项ID[" + inputTsDatasConfig.getId()
                                                + "]，站点关系[" + inputTsDatasConfig.getStationrelate().toString() + "]，站点类型编码[" + inputTsDatasConfig.getStationctgcode() + "]，站点扩展标记[" + inputTsDatasConfig.getStationext()
                                                + "]，是否同要素[" + (inputTsDatasConfig.getElesame() == true) + "]，要素编码[" + inputTsDatasConfig.getElecode() + "]，要素扩展标记[" + inputTsDatasConfig.getEleext()
                                                + "]，取点号时段类型[" + inputTsDatasConfig.getTmsegtype().toString() + "]，指定时段类型[" + inputTsDatasConfig.getTmseg().toString()
                                                + "]，取值字段[" + inputTsDatasConfig.getValuefield().toString() + "]，输入顺序号[" + inputTsDatasConfig.getOdr()
                                                + "]，查询时间段类型[" + inputTsDatasConfig.getTmrange().toString() + "]，起始偏移[" + inputTsDatasConfig.getTmbeginoffset() + "]，截止偏移[" + inputTsDatasConfig.getTmendoffset()
                                                + "]，不包含起始[" + (inputTsDatasConfig.getExcludebegintm() == true) + "]，不包含截止[" + (inputTsDatasConfig.getExcludeendtm() == true)
                                                + "]，需要起始时间往前最近数据[" + (inputTsDatasConfig.getNeedbegintmlatest() == true) + "]，往前偏移[" + inputTsDatasConfig.getBegintmlatestoffset() + "]");
                                        //一个关联的输入点号数据配置可能对应一个或多个点号的数据：先获取站点（集合）、再遍历站点获取点号、根据点号查询时序数据
                                        List<String> stcds = new ArrayList<>();
                                        //根据计算法方法关联的输入数据中的站点关系进行获取
                                        QmsQueryPointDataStationRelationTypeEnum stationRelationType = inputTsDatasConfig.getStationrelate();
                                        if (stationRelationType == QmsQueryPointDataStationRelationTypeEnum.SAME_TO_DEST) {
                                            logger.debug("initPointRelations 根据计算方法和计算目标对象获取第[" + inputTsDatasConfigIndex + "]个输入点号数据序列，输入数据站点编码与目标相同[" + destPointStcd + "]");
                                            //获取数据的站点与计算目标中的站点相同
                                            stcds.add(destPointStcd);
                                        } else {
                                            //如果是要获取关联的站点，关联的站点分类必须指定
                                            if (StringUtils.isBlank(inputTsDatasConfig.getStationctgcode())) {
                                                logger.error("initPointRelations 根据计算方法和计算目标对象获取第[" + inputTsDatasConfigIndex + "]个输入点号数据序列，输入数据站点关系[" + stationRelationType.getName() + "]，关联站点分类编码为空无法获取站点信息");
                                            } else {
                                                //判断关联关系的方向，from还是to
                                                boolean isRelatedFrom = false;
                                                if (stationRelationType == QmsQueryPointDataStationRelationTypeEnum.RELATE_FROM_DEST)
                                                    isRelatedFrom = true;
                                                List<QmsStationRelation> stationRelations = getStationRelations(destPointStcd, isRelatedFrom, inputTsDatasConfig.getStationctgcode(), inputTsDatasConfig.getStationext());
                                                if (stationRelations == null || stationRelations.size() <= 0x0) {
                                                    //获取到关联关系的站点集合为空，可能是关联关系设置错误或输入数据中的配置错误
                                                    logger.warn("initPointRelations 根据计算方法和计算目标对象获取第[" + inputTsDatasConfigIndex + "]个输入点号数据序列，输入数据站点关系[" + stationRelationType.getName()
                                                            + "]，站点分类编码[" + inputTsDatasConfig.getStationctgcode() + "]，站点扩展标记[" + inputTsDatasConfig.getStationext() + "]，获取到的关联站点(集合)为空");
                                                } else {
                                                    logger.debug("initPointRelations 根据计算方法和计算目标对象获取第[" + inputTsDatasConfigIndex + "]个输入点号数据序列，输入数据站点关系[" + stationRelationType.getName()
                                                            + "]，站点分类编码[" + inputTsDatasConfig.getStationctgcode() + "]，站点扩展标记[" + inputTsDatasConfig.getStationext() + "]，获取到的关联站点共[" + stationRelations.size() + "]个");
                                                    //站点关联关系按排序号排序后加入到站点编码集合中，在获取站点关联关系中返回结果已经排序不需要重复排序
                                                    if (isRelatedFrom == false)
                                                        stcds = stationRelations.stream().map(p -> p.getRelatestcd()).collect(Collectors.toList());
                                                    else
                                                        stcds = stationRelations.stream().map(p -> p.getStcd()).collect(Collectors.toList());
                                                }
                                            }
                                        }

                                        //判断获取到的站点集合，遍历逐个站点获取点号
                                        if (stcds.size() <= 0x0) {
                                            logger.debug("initPointRelations 根据计算方法和计算目标对象获取第[" + inputTsDatasConfigIndex + "]个输入点号数据序列，输入数据站点关系[" + stationRelationType.getName()
                                                    + "]，站点分类编码[" + inputTsDatasConfig.getStationctgcode() + "]，站点扩展标记[" + inputTsDatasConfig.getStationext() + "]，未获取到任何关联站点");
                                        } else {
                                            //获取监测要素编码，一个输入数据序列中只能设置唯一一个监测要素编码
                                            String elementCode = destPointElementCode;
                                            //如果输入点号数据系列配置中未配置或无效，默认均使用同目标相同要素编码
                                            if (inputTsDatasConfig.getElesame() == false) {
                                                //输入点号的监测要素与目标不同
                                                if (StringUtils.isBlank(inputTsDatasConfig.getElecode())) {
                                                    logger.warn("initPointRelations 根据计算方法和计算目标对象获取第[" + inputTsDatasConfigIndex + "]个输入点号数据序列，输入数据站点关系[" + stationRelationType.getName()
                                                            + "]，站点分类编码[" + inputTsDatasConfig.getStationctgcode() + "]，站点扩展标记[" + inputTsDatasConfig.getStationext() + "]，获取到的关联站点(集合)为空");
                                                } else {
                                                    //有设置输入点号的监测要素编码
                                                    elementCode = inputTsDatasConfig.getElecode();
                                                }
                                            }

                                            //输入点号数据项中的监测要素编码
                                            String elementExt = inputTsDatasConfig.getEleext();

                                            //构造查询点号数据的时间段需要用于点号的时段类型，对一个输入数据项来说输入的点号时段类型相同，在此根据第一个站点的点号获取一次即可
                                            //查询点号的时段类型需要根据每个点号中的配置来，默认保持与计算目标时段类型一致
                                            QmsPointTimeSegmentEnum queryPointDataTimeSegment = destTmseg;
                                            //如果输入点号数据项中配置为指定的时段类型并且时段类型有效，使用配置的指定时段类型
                                            if (inputTsDatasConfig.getTmsegtype() == QmsQueryPointDataTimeSegmentTypeEnum.DESIGNATED
                                                    && inputTsDatasConfig.getTmseg() != QmsPointTimeSegmentEnum.UN_DEFINE) {
                                                //指定的时段类型有效
                                                queryPointDataTimeSegment = inputTsDatasConfig.getTmseg();
                                                logger.debug("initPointRelations 根据计算方法和计算目标对象获取第[" + inputTsDatasConfigIndex + "]个输入点号数据序列，取点号时段类型[" + inputTsDatasConfig.getTmsegtype().toString()
                                                        + "]，指定时段类型[" + inputTsDatasConfig.getTmseg().toString() + "]，设置查询输入点号数据时段类型为[" + queryPointDataTimeSegment.toString() + "]");
                                            } else if (inputTsDatasConfig.getTmsegtype() == QmsQueryPointDataTimeSegmentTypeEnum.SMALLER_ONE_THAN_DEST) {
                                                //如果输入点号数据项中配置为比目标点号小一个时段类型，获取输入点号的有效时段类型进行计算；
                                                //不需要获取输入点号中的时段类型，直接使用目前时段类型在目标点号中获取小一个时段的时段类型
                                                queryPointDataTimeSegment = QmsPointTimeSegmentEnum.getSmallerOneTimeSegmentEnum(destPointTmsegs, destTmseg);
                                                logger.debug("initPointRelations 根据计算方法和计算目标对象获取第[" + inputTsDatasConfigIndex + "]个输入点号数据序列，取点号时段类型[" + inputTsDatasConfig.getTmsegtype().toString()
                                                        + "]，取计算目标时段类型小一个时段类型，设置查询输入点号数据时段类型为[" + queryPointDataTimeSegment.toString() + "]");
                                            }

                                            //判断获取到的查询点号时段类型
                                            if (queryPointDataTimeSegment == QmsPointTimeSegmentEnum.UN_DEFINE) {
                                                //无效，无效时使用同目标时段类型继续获取输入点号数据
                                                queryPointDataTimeSegment = destTmseg;

                                                logger.warn("initPointRelations 根据计算方法和计算目标对象获取第[" + inputTsDatasConfigIndex + "]个输入点号数据序列，输入数据配置项ID[" + inputTsDatasConfig.getId()
                                                        + "]，取点号时段类型[" + inputTsDatasConfig.getTmsegtype().getName() + "]，指定时段类型[" + inputTsDatasConfig.getTmseg().toString()
                                                        + "]，未能获取到有效的查询数据时段类型，查询时序数据使用时段类型[" + queryPointDataTimeSegment.getName() + "]");
                                            } else {
                                                logger.debug("initPointRelations 根据计算方法和计算目标对象获取第[" + inputTsDatasConfigIndex + "]个输入点号数据序列，输入数据配置项ID[" + inputTsDatasConfig.getId()
                                                        + "]，取点号时段类型[" + inputTsDatasConfig.getTmsegtype().getName() + "]，指定时段类型[" + inputTsDatasConfig.getTmseg().toString()
                                                        + "]，查询时序数据使用时段类型[" + queryPointDataTimeSegment.getName() + "]");
                                            }

                                            //遍历站点编码集合，逐个获取对应点号进行时序数据查询
                                            for (String stcd : stcds) {
                                                QmsPointDto pointDto = null;
                                                //如果获取的点号数据是比目标点号小一个时段类型：判断当前站点编码如果就是目前站点编码则直接使用目标点号；如果当前站点编码与目标站点编码不同才需要从站点与点号关联关系中获取
                                                if (inputTsDatasConfig.getTmsegtype() == QmsQueryPointDataTimeSegmentTypeEnum.SMALLER_ONE_THAN_DEST && StringUtils.equalsIgnoreCase(destPointStcd, stcd)) {
                                                    pointDto = new QmsPointDto();
                                                    BeanUtils.copyProperties(destPointDto, pointDto); //复制对象属性
                                                } else {
                                                    pointDto = getPointDtoByStcdAndElementCode(stcd, elementCode, elementExt);
                                                }

                                                if (pointDto == null) {
                                                    logger.warn("initPointRelations 根据计算方法和计算目标对象获取第[" + inputTsDatasConfigIndex + "]个输入点号数据序列，输入数据配置项ID[" + inputTsDatasConfig.getId() + "]，根据站点编码[" + stcd
                                                            + "]，监测要素编码[" + elementCode + "]，监测要素扩展标记[" + elementExt + "]，未能获取到对应的点号");
                                                } else {
                                                    logger.debug("initPointRelations 根据计算方法和计算目标对象获取第[" + inputTsDatasConfigIndex + "]个输入点号数据序列，输入数据配置项ID[" + inputTsDatasConfig.getId() + "]，根据站点编码[" + stcd
                                                            + "]，监测要素编码[" + elementCode + "]，监测要素扩展标记[" + elementExt + "]，获取到输入数据点号[" + pointDto.getPointid() + "]");

                                                    //关联输入的点号和时段类型：pointDto.getPointid(), queryPointDataTimeSegment
                                                    QmsNumericCalcPointRelation pointRelation = new QmsNumericCalcPointRelation();
                                                    pointRelation.setPointid(destPointid);
                                                    pointRelation.setTmseg(destTmseg);
                                                    pointRelation.setInputPointid(pointDto.getPointid());
                                                    pointRelation.setInputTmseg(queryPointDataTimeSegment);
                                                    pointRelation.setTmrange(inputTsDatasConfig.getTmrange());
                                                    pointRelation.setTmbeginoffset(inputTsDatasConfig.getTmbeginoffset());
                                                    pointRelation.setExcludebegintm(inputTsDatasConfig.getExcludebegintm());
                                                    pointRelation.setTmendoffset(inputTsDatasConfig.getTmendoffset());
                                                    pointRelation.setExcludeendtm(inputTsDatasConfig.getExcludeendtm());
                                                    pointRelation.setNeedbegintmlatest(inputTsDatasConfig.getNeedbegintmlatest());
                                                    pointRelation.setBegintmlatestoffset(inputTsDatasConfig.getBegintmlatestoffset());

                                                    result.add(pointRelation);
                                                }
                                            }
                                        }
                                    }
                                } else {
                                    //计算方法的输入点号配置为空
                                }
                            }
                        }
                    }
                }
            }
        }

        //调试输出点号触发关联关系结果
        if (result.size() > 0x0) {
            logger.debug("initPointRelations 重新初始化构建点号与点号之间关联关系Complete，点号触发关联关系共[" + result.size() + "]条");
            logger.debug("点号ID\t点号时段类型\t关联的输入点号ID\t关联的输入点号时段类型\t查询点号数据时间类型\t起始时间偏移\t不包含起始时间点\t截止时间偏移\t不包含截止时间点\t需要起始时间最近一条数据\t往前时段数");
            for (QmsNumericCalcPointRelation relation : result) {
                logger.debug(relation.getPointid() + "\t" + relation.getTmseg() + "\t" + relation.getInputPointid() + "\t" + relation.getInputTmseg()
                        + "\t" + relation.getTmrange() + "\t" + relation.getTmbeginoffset() + "\t" + relation.getExcludebegintm()
                        + "\t" + relation.getTmendoffset() + "\t" + relation.getExcludeendtm() + "\t" + relation.getNeedbegintmlatest() + "\t" + relation.getBegintmlatestoffset());
            }
        } else {
            logger.debug("initPointRelations 重新初始化构建点号与点号之间关联关系Complete，无任何点号触发关联关系。");
        }

        return result;
    }

    /**
     * 根据指定站点编码、关联站点方向（to、from）、站点分类编码、扩展标记（可以为null）获取站点之间的关联关系集合
     * 返回的结果按照排序号Asc排序后返回
     *
     * @param stcd                       站点编码
     * @param isRelatedFrom              关联站点方向（to、from）
     * @param relateStationCategoryCode  关联的站点分类编码
     * @param relateStationCategoryCode, ext 扩展标记
     * @return java.util.List<qc.module.qms.entity.QmsStationRelation>
     * @author QuCheng Tech
     * @since 2025/9/22
     */
    List<QmsStationRelation> getStationRelations(String stcd, boolean isRelatedFrom, String relateStationCategoryCode, String ext) {
        logger.debug("getStationRelations 根据站点编码获取站点关联关系集合，站点编码[" + stcd + "]，关联站点方向From[" + isRelatedFrom + "]，站点分类编码[" + relateStationCategoryCode + "]，扩展标记[" + ext + "]");
        //站点编码和关联的站点分类编码不能为空，扩展标记可以为空
        if (StringUtils.isNotBlank(stcd) && StringUtils.isNotBlank(relateStationCategoryCode)) {
            List<QmsStationRelation> allStationRelations = stationRelationService.getAllWithCache();
            if (allStationRelations != null && allStationRelations.size() > 0x0) {
                List<QmsStationRelation> filterStationRelations = null;
                //判断扩展标记是否为分别进行筛选
                if (StringUtils.isNotBlank(ext)) {
                    //有扩展标记，精确匹配扩展标记内容相等
                    if (isRelatedFrom == false)//匹配关联的站点
                        filterStationRelations = allStationRelations.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getStcd(), stcd)
                                && StringUtils.equalsIgnoreCase(p.getRelatectgcode(), relateStationCategoryCode) && StringUtils.equalsIgnoreCase(p.getExt(), ext)).collect(Collectors.toList());
                    else//匹配作为关联站点的From
                        filterStationRelations = allStationRelations.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getRelatestcd(), stcd)
                                && StringUtils.equalsIgnoreCase(p.getCtgcode(), relateStationCategoryCode) && StringUtils.equalsIgnoreCase(p.getExt(), ext)).collect(Collectors.toList());
                } else {
                    //扩展标记为空，要匹配扩展标记为空的关联关系
                    if (isRelatedFrom == false)//匹配关联的站点
                        filterStationRelations = allStationRelations.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getStcd(), stcd)
                                && StringUtils.equalsIgnoreCase(p.getRelatectgcode(), relateStationCategoryCode) && StringUtils.isBlank(p.getExt())).collect(Collectors.toList());
                    else//匹配作为关联站点的From
                        filterStationRelations = allStationRelations.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getRelatestcd(), stcd)
                                && StringUtils.equalsIgnoreCase(p.getCtgcode(), relateStationCategoryCode) && StringUtils.isBlank(p.getExt())).collect(Collectors.toList());
                }

                //判断有匹配的关联关系，按关联关系中的排序号排序后返回
                if (filterStationRelations != null && filterStationRelations.size() > 0x0) {
                    logger.debug("getStationRelations 根据站点编码获取站点关联关系集合，站点编码[" + stcd + "]，关联站点方向From[" + isRelatedFrom + "]，站点分类编码[" + relateStationCategoryCode + "]，扩展标记[" + ext + "]，获取到的站点关联关系共[" + filterStationRelations.size() + "]个");
                    return filterStationRelations.stream().sorted(Comparator.comparingInt(QmsStationRelation::getOdr)).collect(Collectors.toList());
                } else {
                    logger.debug("getStationRelations 根据站点编码获取站点关联关系集合，站点编码[" + stcd + "]，关联站点方向From[" + isRelatedFrom + "]，站点分类编码[" + relateStationCategoryCode + "]，扩展标记[" + ext + "]，获取到的站点关联关系集合为空");
                }

            } else {
                logger.debug("getStationRelations 根据站点编码获取站点关联关系集合，站点编码[" + stcd + "]，关联站点方向From[" + isRelatedFrom + "]，站点分类编码[" + relateStationCategoryCode + "]，扩展标记[" + ext + "]，所有站点之间关联关系集合为空");
            }
        }

        return null;
    }

    /**
     * 在站点与点号关联关系中根据站点编码、监测要素编码和测点顺序号获取对应的点号
     *
     * @param stcd        站点编码
     * @param elementCode 监测要素编码
     * @param ext         扩展标记，可以为空或null；完全匹配
     * @return qc.module.qms.algorithm.dto.QmsPointDto
     * @author QuCheng Tech
     * @since 2025/9/11
     */
    QmsPointDto getPointDtoByStcdAndElementCode(String stcd, String elementCode, String ext) {
        logger.debug("getByStcdAndElementCode 根据站点编码和监测要素编码获取点号信息，站点编码[" + stcd + "]，监测要素编码[" + elementCode + "]，扩展标记[" + ext + "]");
        if (StringUtils.isNotBlank(stcd) && StringUtils.isNotBlank(elementCode)) {
            //先根据站点与点号关联关系获取匹配的关联集合，正常情况下集合应该只有1个元素，使用该元素中的点号进行点号信息获取；如果有关联关系集合有多个元素默认使用第1个元素
            List<QmsStationPoint> stationPoints = this.getStationPointsByStcdAndElementCode(stcd, elementCode, ext);
            if (stationPoints != null && stationPoints.size() > 0x0) {
                if (stationPoints.size() > 0x1) {
                    //如果获取到有多个对应的点号时生成警告日志
                    logger.warn("getByStcdAndElementCode 根据站点编码和监测要素编码获取点号信息，站点编码[" + stcd + "]，监测要素编码[" + elementCode
                            + "]，扩展标记[" + ext + "]，获取到的站点与点号关联关系有多个，关联关系数量为[" + stationPoints.size() + "]");
                }

                String pointid = stationPoints.get(0x0).getPointid();
                logger.debug("getByStcdAndElementCode 根据站点编码和监测要素编码获取点号信息，站点编码[" + stcd + "]，监测要素编码[" + elementCode
                        + "]，扩展标记[" + ext + "]，获取到的点号为[" + pointid + "]");
                //根据点号ID获取点号信息
                return getPointDto(pointid);
            } else {
                logger.debug("getByStcdAndElementCode 根据站点编码和监测要素编码获取点号信息，站点编码[" + stcd + "]，监测要素编码[" + elementCode
                        + "]，扩展标记[" + ext + "]，获取到的站点与点号关联关系为空");
            }
        } else {
            logger.warn("getByStcdAndElementCode 根据站点编码和监测要素编码获取点号信息，站点编码[" + stcd + "]，监测要素编码[" + elementCode
                    + "]，扩展标记[" + ext + "]，站点编码或监测要素编码为空无法获取点号信息");
        }

        return null;
    }

    /**
     * 根据指定的站点编码、监测要素编码、扩展标记（可以为null）获取对应的站点与点号关联集合
     *
     * @param stcd        站点编码
     * @param elementCode 监测要素编码
     * @param ext         扩展标记，可以为空或null；完全匹配
     * @return List<QmsStationPoint> 按正常情况返回结果最多有1个元素
     * @author QuCheng Tech
     * @since 2025/9/19
     */
    List<QmsStationPoint> getStationPointsByStcdAndElementCode(String stcd, String elementCode, String ext) {
        logger.debug("getStationPointsByStcdAndElementCode 根据站点编码和监测要素编码获取点号信息集合，站点编码[" + stcd + "]，监测要素编码[" + elementCode + "]，扩展标记[" + ext + "]");
        //站点编码和监测要素编码不能为空，为空时直接返回Null
        if (StringUtils.isNotBlank(stcd) && StringUtils.isNotBlank(elementCode)) {
            List<QmsStationPoint> allStationPoints = stationPointService.getAllWithCache();
            if (allStationPoints != null && allStationPoints.size() > 0x0) {
                //判断扩展标记是否为分别进行筛选
                if (StringUtils.isNotBlank(ext)) {
                    //有扩展标记，精确匹配扩展标记内容相等
                    List<QmsStationPoint> filterStationPoints = allStationPoints.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getStcd(), stcd)
                            && StringUtils.equalsIgnoreCase(p.getElecode(), elementCode) && StringUtils.equalsIgnoreCase(p.getExt(), ext)).collect(Collectors.toList());
                    if (filterStationPoints != null && filterStationPoints.size() > 0x0) {
                        logger.debug("getStationPointsByStcdAndElementCode 根据站点编码和监测要素编码获取点号信息集合，站点编码[" + stcd + "]，监测要素编码[" + elementCode + "]，扩展标记[" + ext + "]，获取到的站点与点号关联关系共[" + filterStationPoints.size() + "]个");
                        return filterStationPoints;
                    } else {
                        logger.debug("getStationPointsByStcdAndElementCode 根据站点编码和监测要素编码获取点号信息集合，站点编码[" + stcd + "]，监测要素编码[" + elementCode + "]，扩展标记[" + ext + "]，获取到的站点与点号关联关系集合为空");
                    }
                } else {
                    //扩展标记为空，要匹配扩展标记为空的关联关系
                    List<QmsStationPoint> filterStationPoints = allStationPoints.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getStcd(), stcd)
                            && StringUtils.equalsIgnoreCase(p.getElecode(), elementCode) && StringUtils.isBlank(p.getExt())).collect(Collectors.toList());
                    if (filterStationPoints != null && filterStationPoints.size() > 0x0) {
                        logger.debug("getStationPointsByStcdAndElementCode 根据站点编码和监测要素编码获取点号信息集合，站点编码[" + stcd + "]，监测要素编码[" + elementCode + "]，无扩展标记，获取到的站点与点号关联关系共[" + filterStationPoints.size() + "]个");
                        return filterStationPoints;
                    } else {
                        logger.debug("getStationPointsByStcdAndElementCode 根据站点编码和监测要素编码获取点号信息集合，站点编码[" + stcd + "]，监测要素编码[" + elementCode + "]，无扩展标记，获取到的站点与点号关联关系集合为空");
                    }
                }
            } else {
                logger.debug("getStationPointsByStcdAndElementCode 根据站点编码和监测要素编码获取点号信息集合，站点编码[" + stcd + "]，监测要素编码[" + elementCode + "]，扩展标记[" + ext + "]，所有站点与点号关联关系集合为空");
            }
        }

        return null;
    }

    /**
     * 根据指定的点号ID获取点号对象DTO
     *
     * @param pointid 点号ID
     * @return qc.module.qms.algorithm.dto.QmsPointDto
     * @author QuCheng Tech
     * @since 2025/9/27
     */
    QmsPointDto getPointDto(String pointid) {
        logger.debug("getPointDto 根据指定点号ID获取点号信息，点号ID[" + pointid + "]");
        if (StringUtils.isNotBlank(pointid)) {
            List<QmsPoint> allPoints = pointService.getAllWithCache();
            if (allPoints != null && allPoints.size() > 0x0) {
                Optional<QmsPoint> point = allPoints.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getPointid(), pointid)).findFirst();
                if (point != null && point.isPresent())
                    return QmsPointMapper.MAPPER.toDto(point.get());
            }
        }

        return null;
    }

    /**
     * 根据点号和时段类型获取适用的计算方法Entity
     *
     * @param point 点号
     * @param tmseg 时段类型
     * @return qc.module.qms.entity.QmsCalcMethod
     * @author QuCheng Tech
     * @since 2025/9/30
     */
    QmsCalcMethod getPointCalcMethod(QmsPointDto point, QmsPointTimeSegmentEnum tmseg) {
        logger.debug("getPointCalcMethod 根据点号获取计算方法");
        if (point == null) {
            logger.error("getPointCalcMethod 根据点号获取计算方法，点号信息为null");
        } else {
            String pointid = point.getPointid();
            //先根据计算方法与点号关联直接获取，如果不存在或者为禁用状态再根据点号中的采集要素进行获取
            List<QmsCalcMethodPoint> pointMethods = this.getCalcMethodPoints(pointid, tmseg);
            if (pointMethods == null || pointMethods.size() < 0x1) {
                //点号没有直接关联的计算方法
                logger.info("getPointCalcMethod 根据点号获取计算方法，点号[" + pointid + "]，时段类型[" + tmseg.toString() + "]，无任何点号直接关联的计算方法");
            } else {
                //逐个判断是否有可用的计算方法，判断关联是启用状态，并且对应的计算方法也是启用状态
                logger.info("getPointCalcMethod 根据点号获取计算方法，点号[" + pointid + "]，时段类型[" + tmseg.toString() + "]，点号直接关联的计算方法共有[" + pointMethods.size() + "]个");
                for (QmsCalcMethodPoint method : pointMethods) {
                    if (method.getIsdisable() == false) {
                        //计算方法与点号关联关系未禁用，获取计算方法判断是否禁用
                        QmsCalcMethod methodEntity = this.getCalcMethod(method.getMethodcode());
                        if (methodEntity != null && methodEntity.getIsdisable() == false) {
                            //有效，使用当前计算方法作为返回的计算方法
                            logger.info("getPointCalcMethod 根据点号获取计算方法，点号[" + pointid + "]，时段类型[" + tmseg.toString() + "]，根据点号直接关联得到的有效计算方法[" + methodEntity.getMethodcode() + "]");

                            return methodEntity;
                        }
                    }
                }
            }

            //判断如果根据点号直接关联获取不到有效计算方法，再根据点号的采集要素进行获取
            logger.debug("getPointCalcMethod 根据点号监测要素获取计算方法，点号[" + pointid + "]，时段类型[" + tmseg.toString() + "]，根据点号直接关联未获取到有效的计算方法，开始根据点号监测要素获取计算方法");

            logger.debug("getPointCalcMethod 根据点号监测要素获取计算方法，点号[" + pointid + "]，时段类型[" + tmseg.toString() + "]，监测要素编码[" + point.getElecode() + "]");
            List<QmsCalcMethodElement> elementMethods = this.getCalcMethodElement(point.getElecode(), tmseg);
            if (elementMethods == null || elementMethods.size() < 0x1) {
                //采集要素没有直接关联的计算方法
                logger.info("getPointCalcMethod 根据点号监测要素获取计算方法，点号[" + pointid + "]，时段类型[" + tmseg.toString() + "]，监测要素编码[" + point.getElecode() + "]，无任何点号所属监测要素关联的计算方法");
            } else {
                //逐个判断是否有可用的计算方法，判断关联是启用状态，并且对应的计算方法也是启用状态
                logger.info("getPointCalcMethod 根据点号监测要素获取计算方法，点号[" + pointid + "]，时段类型[" + tmseg.toString() + "]，监测要素编码[" + point.getElecode() + "]，点号所属监测要素关联的计算方法共有[" + elementMethods.size() + "]个");
                for (QmsCalcMethodElement method : elementMethods) {
                    if (method.getIsdisable() == false) {
                        //计算方法与监测要素关联关系未禁用，获取计算方法判断是否禁用
                        QmsCalcMethod methodEntity = this.getCalcMethod(method.getMethodcode());
                        if (methodEntity != null && methodEntity.getIsdisable() == false) {
                            //有效，使用当前计算方法作为返回的计算方法
                            logger.info("getPointCalcMethod 根据点号监测要素获取计算方法，点号[" + pointid + "]，时段类型[" + tmseg.toString() + "]，监测要素编码[" + point.getElecode() + "]，根据点号所属监测要素关联得到的有效计算方法[" + methodEntity.getMethodcode() + "]");

                            return methodEntity;
                        }
                    }
                }
            }
        }

        return null;
    }

    /**
     * 获取指定点号和时段类型的所有关联的计算方法，不管状态是否禁用
     *
     * @param pointid 点号
     * @param tmseg   时段类型
     * @return java.util.List<qc.module.qms.entity.QmsCalcMethodPoint>
     * @author QuCheng Tech
     * @since 2025/9/9
     */
    List<QmsCalcMethodPoint> getCalcMethodPoints(String pointid, QmsPointTimeSegmentEnum tmseg) {
        if (StringUtils.isNotBlank(pointid) && tmseg != QmsPointTimeSegmentEnum.UN_DEFINE) {
            //点号ID为空或者时段类型错误时无法获取对应计算方法
            List<QmsCalcMethodPoint> allMethods = calcMethodPointService.getAllWithCache();
            if (allMethods != null && allMethods.size() > 0x0) {
                List<QmsCalcMethodPoint> filterMethods = allMethods.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getPointid(), pointid)
                        && p.getTmsegs() != null && p.getTmsegs().intValue() > 0x0 && (p.getTmsegs().intValue() & tmseg.getIndex()) == tmseg.getIndex()).collect(Collectors.toList());
                if (filterMethods != null && filterMethods.size() > 0x0)
                    return filterMethods;
            }
        }

        return null;
    }

    /**
     * 获取指定要素编码和时段类型的所有关联计算方法，不管状态是否禁用
     *
     * @param elementCode 采集要素编码
     * @param tmseg       时段类型
     * @return java.util.List<qc.module.qms.entity.QmsCalcMethodElement>
     * @author QuCheng Tech
     * @since 2025/9/9
     */
    List<QmsCalcMethodElement> getCalcMethodElement(String elementCode, QmsPointTimeSegmentEnum tmseg) {
        if (StringUtils.isNotBlank(elementCode) && tmseg != QmsPointTimeSegmentEnum.UN_DEFINE) {
            //站点分类编码为空、要素编码为空或者时段类型错误时无法获取对应计算方法
            List<QmsCalcMethodElement> allMethods = calcMethodElementService.getAllWithCache();
            if (allMethods != null && allMethods.size() > 0x0) {
                List<QmsCalcMethodElement> filterMethods = allMethods.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getElecode(), elementCode)
                        && p.getTmsegs() != null && p.getTmsegs().intValue() > 0x0 && (p.getTmsegs().intValue() & tmseg.getIndex()) == tmseg.getIndex()).collect(Collectors.toList());
                if (filterMethods != null && filterMethods.size() > 0x0)
                    return filterMethods;
            }
        }

        return null;
    }

    /**
     * 获取指定编码的计算方法，有缓存时从缓存中获取
     *
     * @param methodCode 计算方法编码
     * @return qc.module.qms.entity.QmsCalcMethod
     * @author QuCheng Tech
     * @since 2025/9/11
     */
    QmsCalcMethod getCalcMethod(String methodCode) {
        if (StringUtils.isNotBlank(methodCode)) {
            List<QmsCalcMethod> allMethods = calcMethodService.getAllWithCache();
            if (allMethods != null && allMethods.size() > 0x0) {
                Optional<QmsCalcMethod> method = allMethods.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getMethodcode(), methodCode)).findFirst();
                if (method != null && method.isPresent())
                    return method.get();
            }
        }

        return null;
    }

    /**
     * 获取指定计算方法的输入时序配置，优先从缓存中获取
     *
     * @param methodCode 计算法方法编码
     * @return java.util.List<qc.module.qms.entity.QmsCalcMethodInput>
     * @author QuCheng Tech
     * @since 2025/9/9
     */
    List<QmsCalcMethodInput> getCalcMethodInputs(String methodCode) {
        if (StringUtils.isNotBlank(methodCode)) {
            //计算方法编码为空时无法获取关联的输入时序数据配置
            List<QmsCalcMethodInput> allInputs = calcMethodInputService.getAllWithCache();
            if (allInputs != null && allInputs.size() > 0x0) {
                List<QmsCalcMethodInput> filterMethodInputs = allInputs.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getMethodcode(), methodCode)).collect(Collectors.toList());
                if (filterMethodInputs != null && filterMethodInputs.size() > 0x0)
                    return filterMethodInputs;
            }
        }

        return null;
    }
}
